package jvm规范;

2.【基于栈的字节码解释执行引擎】
在Java语言中，javac编译器完成了程序代码经过词法分析->语法分析->抽象语法树->遍历语法树生成线性的字节码指令流的过程，而解释器在虚拟机的内部。

2.1 基于栈和基于寄存器
Java编译器输出的指令流，基本上是一种基于栈的指令集架构，指令流中的指令大部分都是零地址指令，它们依赖操作数栈进行工作。与之相对的另外一套指令集架构是基于
寄存器的指令集，这些指令依赖寄存器进行工作。

如果要计算”1+1“的结果，基于栈的指令流会是这样的：
iconst_1 //int类型的1入栈
iconst_1 //int类型的1入栈
iadd //栈顶两个int类型出栈，相加，把结果入栈
istore_0 //将栈顶的值出栈放到局部变量表的第0位置的slot中

如果基于寄存器，可能会是这样子的：
mov eax,1 //把eax寄存器的值设为1
add eax,1 //把eax寄存器的值加1，结果保存在eax寄存器
基于栈的指令集是可移植的，而寄存器由硬件直接或间接提供，程序依赖这些硬件寄存器就要受到硬件的约束；但是基于栈的指令集完成相同功能所需的指令数量一般会比寄存器
架构多，而且栈实现在内存之中，频繁的栈访问也就意味着频繁的内存访问，相对于处理器来说，内存始终是执行速度的瓶颈。由于指令数量和内存访问的原因，就导致了栈架构
指令集的执行速度相对来说会慢一些。所有主流物理机的指令集都是基于寄存器的。

2.2 基于栈的解释器执行过程
这里采用书中一样的示例代码，但是出于方便，就不画图了，而是在每个指令后面通过文字备注操作数栈和局部变量表的状态，Java代码如下：

Java
public int calc() {
        int a = 100;
        int b = 200;
        int c = 300;
        return (a + b) * c;
    }
编译之后通过javap查看字节码指令（操作数栈和局部变量表的变化都在备注中，其中描述栈时，右边为栈顶方向）：

public int calc();
    descriptor: ()I
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=4, args_size=1
         0: bipush        100  //将100入栈。栈：100；变量表：0=this
         2: istore_1           //将100出栈，存放到局部变量表第1个slot。栈：空；变量表：0=this,1=100
         3: sipush        200  //将200入栈。栈：200；变量表：0=this，1=100
         6: istore_2           //将200出栈，存放到局部变量表第2个slot。栈：空；变量表：0=this,1=100,2=200
         7: sipush        300  //将300入栈。栈：300；变量表：0=this,1=100,2=200
        10: istore_3           //将300出栈，存放到局部变量表第3个slot。栈：空；变量表：0=this,1=100,2=200，3=300
        11: iload_1            //将局部变量表中第1个slot整型值入栈。栈：100；变量表：0=this,1=100,2=200，3=300
        12: iload_2            //将局部变量表中第2个slot整型值入栈。栈：100,200；变量表：0=this,1=100,2=200，3=300
        13: iadd               //将栈顶两个元素出栈做整型加法，然后把结果入栈。栈：300；变量表：0=this,1=100,2=200，3=300
        14: iload_3            //将局部变量表中第3个slot整型值入栈。栈：300,300；变量表：0=this,1=100,2=200，3=300
        15: imul               //将栈顶两个元素出栈做整型乘法，然后把结果入栈。栈：90000；变量表：0=this,1=100,2=200，3=300
        16: ireturn            //结束方法执行，将栈顶整型值返回给方法调用者
      LineNumberTable:
        line 8: 0
        line 9: 3
        line 10: 7
        line 11: 11
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      17     0  this   Lcom/demo/Main;
            3      14     1     a   I
            7      10     2     b   I
           11       6     3     c   I
从上可以看出，这段代码需要深度为2的操作数栈（参照出栈/入栈过程中栈的最大深度），需要4个slot的局部变量空间（this、a、b、c）

关于各个指令的含义，可以参考官方文档：https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html

上面的执行过程仅仅是一种概念模型，虚拟机最终会对执行过程做一些优化来提高性能，实际的运作过程可能和概念模型差距非常大。因为虚拟机中解析器和即时编译器都会对输入
的字节码进行优化，比如在HotSpot虚拟机中，有很多以”fast_“开头的非标准字节码指令用于合并、替换输入的字节码以提升解释执行的性能，而即时编译器的优化手段更加
繁多。